package com.smartalarmclock.Model;

import com.smartalarmclock.Model.Exceptions.InvalidDeleteDataEntityOperationException;
import com.smartalarmclock.Model.Exceptions.InvalidSaveDataEntityOperationException;
import com.smartalarmclock.Model.Exceptions.ReferenceCannotBeNullException;

import android.content.Context;

final class SingleReference<T extends IReferencedDataEntity> extends Reference<T> {
	
	// Members
	
	private IReferencingDataEntity m_ReferencingEntity;
	
	private boolean m_IsNullable;
	private T m_CurrentReference;
	
	// Constructors
	
	/**
	 * Constructor
	 * @param referencingEntity the referencing entity
	 * @param currentReference the referenced data entity
	 * @param isNullable indicates if the reference can be null or not
	 * @param referencedType the type of the reference (the type in the DB)
	 * @param deleteMethod the method to use when the referencing entity is deleted
	 * (the delete method should be set to delete if each reference is referenced by one referencing entity only)
	 */
	public SingleReference(
		IReferencingDataEntity referencingEntity,
		T currentReference,
		boolean isNullable,
		Class<?> referencedType,
		DeleteMethod deleteMethod) {
		super(referencedType, deleteMethod);
		m_ReferencingEntity = referencingEntity;
		
		m_IsNullable = isNullable;
		
		m_CurrentReference = currentReference;
		if (m_CurrentReference != null) {
			m_CurrentReference.RegisterReference(m_ReferencingEntity);
		}
	}
	
	/**
	 * Constructor for nullable references only! The current reference is null.
	 * @param referencingEntity the referencing entity
	 * @param referencedType the type of the reference (the type in the DB)
	 * @param deleteMethod the method to use when the referencing entity is deleted
	 * (the delete method should be set to delete if each reference is referenced by one referencing entity only)
	 */
	public SingleReference(
		IReferencingDataEntity referencingEntity,
		Class<?> referencedType,
		DeleteMethod deleteMethod) {
		this(referencingEntity, null, true, referencedType, deleteMethod);
	}
	
	// Public Methods
	
	/**
	 * @return the referenced data entity
	 */
	public T GetReference() {
		return m_CurrentReference;
	}
	
	/**
	 * Sets a new reference of the matching type.
	 * @param newReference the new referenced entity
	 * @throws ReferenceCannotBeNullException if the reference is set to null when it cannot be null.
	 */
	public void Set(T newReference) throws ReferenceCannotBeNullException {
		if (!m_IsNullable && newReference == null) {
			throw new ReferenceCannotBeNullException(m_ReferencingEntity.getClass(), m_ReferencingEntity.GetId(), m_CurrentReference.getClass());
		}
		
		HandleOldReference(m_CurrentReference, m_ReferencingEntity);
		HandleNewReference(newReference, m_ReferencingEntity);
		
		m_CurrentReference = newReference;
	}
	
	// Overriding Methods
	
	/**
	 * Saves the referenced entity.
	 * @param context the context of the request
	 * @throws InvalidSaveDataEntityOperationException if the save operation is invalid.
	 */
	@Override
	public void SaveReferences(Context context) throws InvalidSaveDataEntityOperationException {
		DeleteAllOldReferences(context);
		if (m_CurrentReference != null) {
			m_CurrentReference.Save(context);
		}
		
		// Saving the reference mapping to the DB.
		SmartAlarmClockDB.GetInstance().AddReferenceMapping(context, m_ReferencingEntity.getClass(), m_referencedType);
	}
	
	/**
	 * Deletes the referenced data entity from the DB only if the delete method is to delete
	 * (the delete method should be set to delete if each reference is referenced by one referencing entity only)
	 * @param context the context of the request
	 */
	@Override
	public void OnDeleteReferencingEntity(Context context) {
		DeleteAllOldReferences(context);
		
		if (m_DeleteMethod == DeleteMethod.Delete) {
			try {
				m_CurrentReference.Delete(context);
			}
			catch (InvalidDeleteDataEntityOperationException e) {
				// This cannot happen since the delete method is "Delete", which means that
				// there are no other entities referencing this entity. So the delete operation cannot fail.
			}
			catch (InvalidSaveDataEntityOperationException e) {
				// Because there are no other entities referencing this entity, no entities
				// will be saved while deleting this reference.
			}
		}
	}
}
